﻿/***************************************************************************
 *    
 *      Copyright (c) 2009,2010 KaiTrade LLC (registered in Delaware)
 *                     All Rights Reserved Worldwide
 *
 * STRICTLY PROPRIETARY and CONFIDENTIAL
 *
 * WARNING:  This file is the confidential property of KaiTrade LLC For
 * use only by those with the express written permission and license from
 * KaiTrade LLC.  Unauthorized reproduction, distribution, use or disclosure 
 * of this file or any program (or document) is prohibited. 
 * 
 ***************************************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace K2AlgoSet
{
    /// <summary>
    /// This class impliments a bracket order execution strategy - it inherets methods from ORAlgbase
    /// </summary>
    public class K2Bracket : ORAlgBase
    {
        public enum AlgState { Loaded, WaitingStart, InitialOrder, StopLoss, StopProfit, Ended, error };

        public enum FillState { NoFills, PartialFilled, Filled };
        /// <summary>
        /// used for lock()
        /// </summary>
        private Object m_Token1 = new Object();
        private Object m_CancelQueueLock = new Object();

        /// <summary>
        /// ID of this algo
        private string m_ID;

        /// <summary>
        /// The leading order - i.e. the first we submit, we will bracket this with a 
        /// Stop and Profit order
        /// </summary>
        private string m_LeadOrder = "";

        /// <summary>
        /// Track the state of the algo
        /// </summary>
        private AlgState m_AlgState = AlgState.Loaded;

        /// <summary>
        /// flag to indicate that the algo has been entered
        /// </summary>
        private bool m_IsEntered = false;

        /// <summary>
        /// Track all the orders places by a run of the algo
        /// </summary>
        private KaiTrade.Interfaces.Order[] m_CurrentOrders;

        private double? m_ProfitPriceDelta;
        private double? m_StopPriceDelta;

        /// <summary>
        /// Logger
        /// </summary>
        public log4net.ILog m_AlgLog = log4net.LogManager.GetLogger("KaiTrade");

        public K2Bracket()
        {
            Reset();
        }

        /// <summary>
        /// Reset the alg to its starting state
        /// </summary>
        public override void Reset()
        {
            try
            {
                Info("Reset:");
               
                m_LeadOrder = "";
               
                m_CurrentOrders = new KaiTrade.Interfaces.Order[3];
                m_CurrentOrders[0] = null;
                m_CurrentOrders[1] = null;
                m_CurrentOrders[2] = null;

                m_AlgState = AlgState.Loaded;
            }
            catch (Exception myE)
            {
                m_Log.Error("override void Reset", myE);
            }
        }


        public override string ToString()
        {
            string myInfo = "";
            try
            {
                string myStrategyName = "NONE";
                string myRunId = "NONE";
                if (m_Strategy != null)
                {
                    myStrategyName = m_Strategy.UserName;
                    myRunId = m_Strategy.RunIdentifier;
                }

                myInfo = string.Format("Strat Name={0} RunIdent={1} ID={2} Entered={3} ", myStrategyName, myRunId,  m_ID, m_IsEntered);
                myInfo += string.Format("StopPriceDelta={0} ProfitPriceDelta={1} ", m_StopPriceDelta.ToString(), m_ProfitPriceDelta.ToString());
               
            }
            catch (Exception myE)
            {
            }
            return myInfo;

        }

        /// <summary>
        /// Get the ATDL that defines what paramters we need
        /// </summary>
        /// <returns></returns>
        public override string GetAtdl()
        {
            try
            {
                K2AtdlTools.K2AtdlBuilder builder = new K2AtdlTools.K2AtdlBuilder();
                K2AtdlTools.K2AtdlStrategyBuilder strategyBuilder = builder.AddStrategyBuilder("K2Braket", "K2Braket", "D", "KaiTrade LLC");

                AddStrategyParameters(strategyBuilder);
                if (m_ProfitPriceDelta.HasValue)
                {
                    strategyBuilder.AddParameter("KKORAlg1", "ProfitPriceDelta", K2AtdlTools.ADTLType.Float, 0, true, m_ProfitPriceDelta.Value.ToString(), "0", "100");
                }
                else
                {
                    strategyBuilder.AddParameter("KKORAlg1", "ProfitPriceDelta", K2AtdlTools.ADTLType.Float, 0, true, "0", "0", "100");
                }

                if(m_StopPriceDelta.HasValue)
                {
                    strategyBuilder.AddParameter("KKORAlg1", "StopPriceDelta", K2AtdlTools.ADTLType.Float, 0, true, m_StopPriceDelta.Value.ToString(), "", "");
                }
                else
                {
                    strategyBuilder.AddParameter("KKORAlg1", "StopPriceDelta", K2AtdlTools.ADTLType.Float, 0, true, "0", "", "");
                }

                strategyBuilder.AddDefaultLayout();

                return builder.GetAtdlAsString();
            }
            catch (Exception myE)
            {
                m_Log.Error("GetAtdl", myE);
            }
            return "";
        }
        /// <summary>
        /// Get a list of avalaible parameters for the alg
        /// </summary>
        /// <returns></returns>
        public override List<string> GetParameterNames()
        {
            List<string> myRet = base.GetParameterNames();
            myRet.Add("ProfitPriceDelta");
            myRet.Add("StopPriceDelta");
            return myRet;
        }

        /// <summary>
        /// Set a alg setting by name, used to set parameters used by the alg
        /// </summary>
        /// <param name="myValue"></param>
        public override void SetParameterValue(string myName, string myValue, string runIdentifier)
        {
            try
            {
                base.SetParameterValue(myName, myValue, runIdentifier);

                switch (myName)
                {
                    case "ProfitPriceDelta":
                        m_ProfitPriceDelta = double.Parse(myValue);
                        break;
                    case "StopPriceDelta":
                        m_StopPriceDelta = double.Parse(myValue);
                        break;      
                    default:
                        break;
                }

            }
            catch (Exception myE)
            {
                m_Log.Error("SetParameterValue", myE);
            }
        }

        /// <summary>
        /// Get a setting value by name - used to get alg parameters
        /// </summary>
        /// <param name="myName"></param>
        /// <returns></returns>
        public override string GetParameterValue(string myName)
        {
            string myRet = "";
            try
            {
                switch (myName)
                {
                    case "ProfitPriceDelta":
                        myRet = m_ProfitPriceDelta.ToString();
                        break;
                    case "StopPriceDelta":
                        myRet = m_StopPriceDelta.ToString();
                        break;         
                    default:
                        // let the base class do this
                        myRet = base.GetParameterValue(myName);
                        break;
                }
            }
            catch (Exception myE)
            {
                m_Log.Error("GetParameterValue", myE);
            }

            return myRet;
        }


        /// <summary>
        /// Validate the Algorithim 
        /// </summary>
        /// <returns>true if the algo state is valid</returns>
        private bool validate()
        {
            bool myRet = false;

            try
            {
                ///Check that the parameters are set
                if((m_StopPriceDelta.HasValue) &&(m_ProfitPriceDelta.HasValue))
                {
                    myRet = true;
                }
            }
            catch (Exception myE)
            {
                m_Log.Error("validate", myE);
            }
            return myRet;
        }

        /// <summary>
        /// Set the algo state and update the strategy informatiomn
        /// </summary>
        /// <param name="myNewState"></param>
        private void setAlgState(AlgState myNewState)
        {
            try
            {
                if (m_AlgState != myNewState)
                {
                    string myMsg = "setAlgState:oldstate:" + m_AlgState.ToString() + " newState:" + myNewState.ToString();
                    m_Strategy.Info = myMsg;
                    m_AlgLog.Info(myMsg);
                    m_AlgState = myNewState;

                }
            }
            catch (Exception myE)
            {
                m_Log.Error("setAlgState", myE);
            }
        }

        /// <summary>
        /// Record some info from the algo
        /// </summary>
        /// <param name="msg"></param>
        private void Info(string msg)
        {
            try
            {
                if (m_Strategy.Info != msg)
                {
                    m_Log.Info(msg + ":" + this.ToString());
                    m_Strategy.Info = msg;
                }
            }
            catch (Exception myE)
            {
                m_AlgLog.Error("Info", myE);
            }
        }

        /// <summary>
        /// Enter the algo - action depends on implimenting class
        /// This notifies the algo that the strategy has been entered
        /// </summary>
        public override void Enter(KaiTrade.Interfaces.Strategy myStrategy)
        {
            try
            {
                //Check that the algo state is loaded(ready to use) or Ended( a previous run has finished)
                if ((m_AlgState == AlgState.Loaded) || (m_AlgState == AlgState.Ended))
                {
                    if (!validate())
                    {
                        throw new Exception("Invalid start paramters");
                    }
                    m_IsEntered = true;

                    this.Strategy = myStrategy;
                    Info("Enter");

                    setAlgState(AlgState.WaitingStart);

                }
                else
                {
                    m_Strategy.Info = "You can only enter a loaded or ended alg";
                    throw new Exception("You can only enter a loaded or ended alg");
                }

            }
            catch (Exception myE)
            {
                m_AlgLog.Error("override void Enter", myE);
                throw myE;
            }

        }

        /// <summary>
        /// Start the algo running this may result in orders being placed
        /// this is called after the Enter
        /// </summary>
        public override void Run(KaiTrade.Interfaces.Strategy myStrategy)
        {
            try
            {
                processInitialOrder();
            }
            catch (Exception myE)
            {
                m_AlgLog.Error("override void Runb", myE);
                throw myE;
            }          
        }

        /// <summary>
        /// Exit the strategy - action depends on implimenting class
        /// </summary>
        public override void Exit(KaiTrade.Interfaces.Strategy myStrategy)
        {
            try
            {
                Info("Exit");
                this.setAlgState(AlgState.Ended);
                m_Strategy.State = KaiTrade.Interfaces.StrategyState.complete;

            }
            catch (Exception myE)
            {
                m_Log.Error("override void Exit", myE);
            }
        }

       
        /// <summary>
        /// This will submit the initial order
        /// </summary>
        private void processInitialOrder()
        {
            try
            {
                Info("processInitialOrder");
                
                // we only shopuld do this once so we can check that the order is null
                if (m_CurrentOrders[0] == null)
                {
                    // first order must not be a stop
                    if (this.Strategy.OrdType.getValue() == QuickFix.OrdType.STOP)
                    {

                        throw new Exception("The initial order should not be a STOP");
                    }

                    double qty = m_Strategy.Qty;
                    m_LeadOrder = Submit(m_Strategy, qty, qty, m_Strategy.Price, m_Strategy.Price, KaiUtil.QFUtils.DecodeSide(m_Strategy.Side),KaiUtil.QFUtils.DecodeOrderType( m_Strategy.OrdType), KaiTrade.Interfaces.TimeType.DAY, "", m_Strategy.Account, "");

                    m_CurrentOrders[0] = Factory.Instance().AppFacade.Factory.GetOrderManager().GetOrder(m_LeadOrder);

                    m_Strategy.LastOrdIdentity = m_LeadOrder;
                    addOrder2Grp(m_Strategy, m_LeadOrder);

                    setAlgState(AlgState.InitialOrder);
                }
                else
                {
                    throw new Exception("Initial order already placed for this run:");
                }
                m_AlgLog.Info("Initial order submitted:" + this.ToString());

            }
            catch (Exception myE)
            {
                m_AlgLog.Error("processInitialOrder", myE);
            }
        }

        /// <summary>
        /// Handle an reject report for an order belonging to a strategy
        /// the primitive handling of the exec report on the order itself will have been done
        /// reject reports always relate to an actual order in the market
        /// </summary>
        /// <param name="myExec">fix execution report</param>
        /// <param name="myOrd">order the execution report applies to </param>
        public override void HandleReject(QuickFix.Message myReject, KaiTrade.Interfaces.Strategy myStrategy, KaiTrade.Interfaces.Order myOrd)
        {
            try
            {
                m_AlgLog.Error("HandleReject:");
                m_Strategy.Info = "Order rejected";
            }
            catch (Exception myE)
            {
                m_AlgLog.Error("HandleReject", myE);
            }
        }

        /// <summary>
        /// Handle an Exec report for an order belonging to a strategy
        /// the primitive handling of the exec report on the order itself will have been done before we get here
        /// exec reports always relate to an actual order in the market
        /// </summary>
        /// <param name="myExec">fix execution report</param>
        /// <param name="myOrd">order the execution report applies to </param>
        public override void HandleExecReport(QuickFix.Message myExec, KaiTrade.Interfaces.Strategy myStrategy, KaiTrade.Interfaces.Order myOrd)
        {
            try
            {
                lock (this)
                {
                    m_AlgLog.Info("HandleExecReport:" + "ordmne=" + myOrd.Mnemonic + ":" + this.ToString());
                    if (myOrd.Identity == m_LeadOrder)
                    {
                        if ((myOrd.OrdStatus.getValue() == QuickFix.OrdStatus.PARTIALLY_FILLED) || (myOrd.OrdStatus.getValue() == QuickFix.OrdStatus.FILLED))
                        {
                            // since we have a fill submit secondary orders
                            processSecondaryOrders(myOrd.LastQty.getValue(), myOrd.LastPx.getValue());
                        }
                    }
                    

                }

            }
            catch (Exception myE)
            {
                m_AlgLog.Error("HandleExecReport", myE);
            }

        }

        /// <summary>
        /// Process the secondary orders - in this case a Stop and Profit order
        /// </summary>
        /// <param name="qty">qty to place</param>
        /// <param name="price">base price (taken of the fill)</param>
        private void processSecondaryOrders(double qty, double price)
        {
            try
            {

                m_AlgLog.Info("processSecondaryOrders:" + this.ToString());

                if (m_AlgState != AlgState.InitialOrder)
                {
                    throw new Exception("Must have placed initial order");
                }
                
                string myID;
                double stopPrice = price;
                double profitPrice = price;

                // invert the side for the stop and profit orders
                string side = "";
                if (m_Strategy.Side.getValue() == QuickFix.Side.BUY)
                {
                    side = KaiTrade.Interfaces.Side.SELL;
                    stopPrice = price - m_StopPriceDelta.Value;
                    profitPrice = price + m_ProfitPriceDelta.Value;
                }
                else
                {
                    side = KaiTrade.Interfaces.Side.BUY;
                    stopPrice = price + m_StopPriceDelta.Value;
                    profitPrice = price - m_ProfitPriceDelta.Value;
                }

                string orderGroup = "K2B" + DateTime.Now.Ticks;

                // submit a KSTOP
                myID = Submit(m_Strategy, qty, qty, price, stopPrice, side, KaiTrade.Interfaces.OrderType.MARKET, KaiTrade.Interfaces.TimeType.DAY, "", m_Strategy.Account, KaiTrade.Interfaces.OrderType.KSTOP);
                m_CurrentOrders[1] = Factory.Instance().AppFacade.Factory.GetOrderManager().GetOrder(myID);

                // set the order's OCA (one cancel's all group name)
                m_CurrentOrders[1].OCAGroupName = orderGroup;
                m_Strategy.LastOrdIdentity = myID;
                addOrder2Grp(m_Strategy, myID);


                // submit a KTarget
                myID = Submit(m_Strategy, qty, qty, price, profitPrice, side, KaiTrade.Interfaces.OrderType.MARKET, KaiTrade.Interfaces.TimeType.DAY, "", m_Strategy.Account, KaiTrade.Interfaces.OrderType.KTARGET);
                m_CurrentOrders[2] = Factory.Instance().AppFacade.Factory.GetOrderManager().GetOrder(myID);
                
                // set the order's OCA (one cancel's all group name)
                m_CurrentOrders[2].OCAGroupName = orderGroup;
               
                m_Strategy.LastOrdIdentity = myID;
                addOrder2Grp(m_Strategy, myID);


            }
            catch (Exception myE)
            {
                m_AlgLog.Error("processSecondaryOrders", myE);
            }
        }

       


    }
}
